package org.grayrabbit.cms.plugin.loaders.classloading;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;

public class NonLockingJarUrlConnection extends URLConnection {

	private URL baseResource;
	private String[] segments;
	private InputStream in;

	  NonLockingJarUrlConnection(URL url)throws IOException
	  {
		super(url = normaliseURL(url));
		String baseText = url.getPath();
		int bangLoc = baseText.indexOf("!");
		String baseResourceText = baseText.substring(0, bangLoc);
		String extraText = "";
		if ((bangLoc <= baseText.length() - 2)&& (baseText.charAt(bangLoc + 1) == '/')) {
			if (bangLoc + 2 == baseText.length()) {
				extraText = "";
			} else {
				extraText = baseText.substring(bangLoc + 1);
			}
		} else {
			throw new MalformedURLException("No !/ in url: "+ url.toExternalForm());
		}
		List<String> segments = new ArrayList<String>();
		StringTokenizer tokens = new StringTokenizer(extraText, "!");
		while (tokens.hasMoreTokens()) {
			segments.add(tokens.nextToken());
		}
		this.segments = segments.toArray(new String[segments.size()]);
		this.baseResource = new URL(baseResourceText);
	  }

	protected static URL normaliseURL(URL url) throws MalformedURLException {
		String text = normalizeUrlPath(url.toString());
		if (!text.startsWith("jar:")) {
			text = "jar:" + text;
		}

		if (text.indexOf('!') < 0) {
			text = text + "!/";
		}

		return new URL(text);
	}

	protected String[] getSegments() {
		return this.segments;
	}

	protected URL getBaseResource() {
		return this.baseResource;
	}

	public void connect() throws IOException {
		if (this.segments.length == 0) {
			setupBaseResourceInputStream();
		} else {
			setupPathedInputStream();
		}
	}

	protected void setupBaseResourceInputStream() throws IOException {
		this.in = getBaseResource().openStream();
	}

	protected void setupPathedInputStream() throws IOException {
		InputStream curIn = getBaseResource().openStream();
		for (int i = 0; i < this.segments.length; i++) {
			curIn = getSegmentInputStream(curIn, this.segments[i]);
		}
		this.in = curIn;
	}

	protected InputStream getSegmentInputStream(InputStream baseIn,String segment) throws IOException {
		JarInputStream jarIn = new JarInputStream(baseIn);
		JarEntry entry = null;
		while (jarIn.available() != 0) {
			entry = jarIn.getNextJarEntry();
			if (entry == null) {
				break;
			}

			if (("/" + entry.getName()).equals(segment)) {
				return jarIn;
			}
		}
		throw new IOException("unable to locate segment: " + segment);
	}

	  public InputStream getInputStream() throws IOException
	  {
	    if (this.in == null)
	    {
	      connect();
	    }
	    return this.in;
	  }

	public JarFile getJarFile() throws IOException {
		String url = this.baseResource.toExternalForm();
		if (url.startsWith("file:/")) {
			url = url.substring(6);
		}
		return new JarFile(URLDecoder.decode(url, "UTF-8"));
	}

	private static String normalizeUrlPath(String name) {
		if (name.startsWith("/")) {
			name = name.substring(1);
		}
		int i = name.indexOf("/..");
		if (i > 0) {
			int j = name.lastIndexOf("/", i - 1);
			name = name.substring(0, j) + name.substring(i + 3);
		}
		return name;
	}
}
